 /* Copyright (C)
  * 2024 ZKLX Optoelectronic Technology Co.LTD
  * All rights reserved.
  */
 /**
  * @file cam_basler_ctrl.cpp
  * @brief basler相机控制接口实现
  * @author xuke
  * @version 1.0
  * @date 2024-06-11
  */

#include <chrono>
#include <sstream>
#include <cmath>
#include <thread>
#include "cam_basler_ctrl.h"
#include "lx_elog/finsh.h"
#define LOG_TAG cam_basler_ctrl
#include "lx_elog/stor_elog.h"

/*--------------------------------extern--------------------------------------------*/
extern BaslerCamCtrl *g_basler_cam;

/*--------------------------------globals-------------------------------------------*/

/*--------------------------------locals--------------------------------------------*/

/*--------------------------------funcs---------------------------------------------*/
/*
 *  0 -- normal
 * -1 -- abnormal
*/
BaslerCamCtrl::BaslerCamCtrl() : cameraSerial(CAMERA_DEVICE, 9600, 8, 1), is_serial_opened(false)
{
}

int BaslerCamCtrl::CameraInitial(int speed)
{
	cameraSerial.speed = speed;

	std::cout << "<5>open basler serial." << std::endl;
	int res = cameraSerial.open();
	if (res != 0) {
		res = cameraSerial.open();
		if (res != 0) {
			std::cerr << "<3>open basler serial error, code: " << res << std::endl;
			return -1;
		}
	}

	is_serial_opened = true;
	return 0;
}

void BaslerCamCtrl::CameraClose()
{
	is_serial_opened = false;
	cameraSerial.close();
}

// get寄存器值
uint32_t BaslerCamCtrl::GetAddressValue(uint32_t address)
{
	if (!is_serial_opened) {
		return 0;
	}
	static const unsigned int slength = 8;
	unsigned char CameraDataSend[slength] = {0};
	unsigned char recvBuf[16] = {0};
	uint32_t *ret_val = 0;
	log_d("to read reg[0x%x]", address);

	//BFS
	CameraDataSend[0] = 0x01;
	//FTF
	CameraDataSend[1] = 0x09; //opCode=read, AddrLen=32bit
	//DataLen
	CameraDataSend[2] = 0x04;
	//Address
	CameraDataSend[3] = (address & 0xFF);
	CameraDataSend[4] = (address & 0xFF00) >> 8;
	CameraDataSend[5] = (address & 0xFF0000) >> 16;
	CameraDataSend[6] = (address & 0xFF000000) >> 24;
	//Data
	//BCC
	//帧尾
	CameraDataSend[7] = 0x03;

	//发送指令并等待响应
	int res = cameraSerial.duplexPairs(CameraDataSend, recvBuf, slength, sizeof(recvBuf), 50);
	ret_val = (uint32_t*)&recvBuf[4];
	log_d("recv len=%d bytes, ret_val=%d, [0x%x]", recvBuf[3], *ret_val, *ret_val);
	return *ret_val;
}

// set寄存器值
int BaslerCamCtrl::SetAddressValue(uint32_t address, uint32_t data)
{
	if (!is_serial_opened) {
		return 0;
	}
	static const unsigned int slength = 12;
	unsigned char CameraDataSend[slength] = {0};
	unsigned char recvBuf[16] = {0};
	//log_d("to write reg[0x%x] value=%d (%x)", address, data, data);

	//BFS
	CameraDataSend[0] = 0x01;
	//FTF
	CameraDataSend[1] = 0x01; //opCode=write, AddrLen=32bit
	//DataLen
	CameraDataSend[2] = 0x04;
	//Address
	CameraDataSend[3] = (address & 0xFF);
	CameraDataSend[4] = (address & 0xFF00) >> 8;
	CameraDataSend[5] = (address & 0xFF0000) >> 16;
	CameraDataSend[6] = (address & 0xFF000000) >> 24;
	//Data
	CameraDataSend[7] = (data & 0xFF);
	CameraDataSend[8] = (data & 0xFF00) >> 8;
	CameraDataSend[9] = (data & 0xFF0000) >> 16;
	CameraDataSend[10] = (data & 0xFF000000) >> 24;
	//BCC
	//帧尾
	CameraDataSend[11] = 0x03;

	//发送指令并等待响应
	int res = cameraSerial.duplexPairs(CameraDataSend, recvBuf, slength, sizeof(recvBuf), 50);
	if (res != 0) {
		return -2;
	}
	return 0;
}

//自动曝光时间上限
int BaslerCamCtrl::SetAutoExposureUpperLimit(uint32_t value)
{
	return SetAddressValue(0x00070080+0x04, value);
}

//自动曝光时间下限
int BaslerCamCtrl::SetAutoExposureLowerLimit(uint32_t value)
{
	return SetAddressValue(0x00070060+0x04, value);
}

//自动曝光目标灰度值
int BaslerCamCtrl::SetAutoTargetValue(uint32_t value)
{
	return SetAddressValue(0x00070000+0x04, value);
}

//曝光模式
int BaslerCamCtrl::SetExposureMode(int mode)
{
	return SetAddressValue(0x00040420+0x04, mode); //0-off,1-onece,2-continues
}
int BaslerCamCtrl::GetExposureMode()
{
	return GetAddressValue(0x00040420+0x04); //0-off,1-onece,2-continues
}

//set exposure time
int BaslerCamCtrl::SetExposureTime(unsigned int time)
{
	if (time < MIN_EXPTIME || time > MAX_EXPTIME) {
		return -1;
	}

	return SetAddressValue(0x00040460+0x04, time);
}

int BaslerCamCtrl::SetImageWidth(unsigned int width)
{
	if (width < 1 || width > 2048) {
		return -1;
	}

	return SetAddressValue(0x00030200+0x04, width);
}

int BaslerCamCtrl::SetImageHeight(unsigned int height)
{
	if (height < 1 || height > 2048) {
		return -1;
	}

	return SetAddressValue(0x00030220+0x04, height);
}

int BaslerCamCtrl::SetCenterOffsetX(unsigned int x)
{
	if (x < 0 || x > 2048) {
		return -1;
	}
	return SetAddressValue(0x00030240+0x04, x);
}
int BaslerCamCtrl::GetCenterOffsetX()
{
	return GetAddressValue(0x00030240+0x04);
}

int BaslerCamCtrl::SetCenterOffsetY(unsigned int y)
{
	if (y < 0 || y > 2048) {
		return -1;
	}
	return SetAddressValue(0x00030260+0x04, y);
}
int BaslerCamCtrl::GetCenterOffsetY()
{
	return GetAddressValue(0x00030260+0x04);
}

int BaslerCamCtrl::SetCenterX(unsigned int x)
{
	return SetAddressValue(0x00030280+0x04, x);
}

int BaslerCamCtrl::SetCenterY(unsigned int y)
{
	return SetAddressValue(0x000302A0+0x04, y);
}

// Analog Gain
int BaslerCamCtrl::SetAnalogGain(unsigned int gain)
{
	if (gain < MIN_AGAIN || gain > MAX_AGAIN) {
		return -1;
	}
	return SetAddressValue(0x00020020+0x04, gain);
}

// Digital Gain
int BaslerCamCtrl::SetDigitalGain(float gain)
{
	unsigned int analogGain = (unsigned int) gain * 33;
	return SetAnalogGain(analogGain);
}
int BaslerCamCtrl::GetDigitalGain(float& gain)
{
	unsigned int analogGain = GetAnalogGain();
	gain = (float) (analogGain / 33);
	return 0;
}

// Gain Mode
int BaslerCamCtrl::SetGainMode(int mode)
{
	return SetAddressValue(0x00020000+0x04, mode);
}
int BaslerCamCtrl::GetGainMode()
{
	return GetAddressValue(0x00020000+0x04);
}

//get exposure time
int BaslerCamCtrl::GetExposureTime()
{
	return GetAddressValue(0x00040460+0x04);
}
//get min exposure time
int BaslerCamCtrl::GetMinExposureTime()
{
	return GetAddressValue(0x00040460+0x08);
}
//get max exposure time
int BaslerCamCtrl::GetMaxExposureTime()
{
	return GetAddressValue(0x00040460+0x0C);
}

//get analogGain
int BaslerCamCtrl::GetAnalogGain()
{
	return GetAddressValue(0x00020020+0x04);
}
//get min analogGain
int BaslerCamCtrl::GetMinAnalogGain()
{
	return GetAddressValue(0x00020020+0x08);
}
//get max analogGain
int BaslerCamCtrl::GetMaxAnalogGain()
{
	return GetAddressValue(0x00020020+0x0C);
}

//get Image Width
int BaslerCamCtrl::GetImageWidth()
{
	return GetAddressValue(0x00030200+0x04);
}
//get Image Height
int BaslerCamCtrl::GetImageHeight()
{
	return GetAddressValue(0x00030220+0x04);
}

//baud rate
int BaslerCamCtrl::SetBaudRate(unsigned int rate)
{
	uint32_t rateVal = 0x14;
	//数据
	if (rate == 115200) {
		rateVal = 0x14;
	} else if (rate == 460800) {
		rateVal = 0x16;
	} else {
		rateVal = 0x0F;
	}
	return SetAddressValue(0x00000700+0x04, rateVal);
}
int BaslerCamCtrl::GetBaudRate(unsigned int *rate)
{
	uint32_t recv_val = GetAddressValue(0x00000700+0x04);
	switch (recv_val&0xFF) {
		case 0x0F :
			*rate = 9600;
			break;
		case 0x11 :
			*rate = 19200;
			break;
		case 0x12 :
			*rate = 38400;
			break;
		case 0x13 :
			*rate = 57600;
			break;
		case 0x14 :
			*rate = 115200;
			break;
		case 0x16 :
			*rate = 460800;
			break;
		default :
			return -1;
	}
	return 0;
}

int BaslerCamCtrl::SetCLTapGeometry(int taps)
{
	int tapValue = 14;
	switch(taps){
		case 1:
			tapValue = 0;
			break;
		case 2:
			tapValue = 1;
			break;
		case 3:
			tapValue = 7;
			break;
		case 8:
			tapValue = 12;
			break;
		case 10:
			tapValue = 14;
			break;
		default:
			break;
	}
	return SetAddressValue(0x00000720+0x04, tapValue);
}

int BaslerCamCtrl::GetCLTapGeometry()
{
	uint32_t taps = 1;
	switch (GetAddressValue(0x00000720+0x04)) {
		case 0:
			taps = 1;
			break;
		case 1:
			taps = 2;
			break;
		case 7:
			taps = 3;
			break;
		case 12:
			taps = 8;
			break;
		case 14:
			taps = 10;
			break;
		default:
			break;
	}
	return taps;
}

//保存配置
int BaslerCamCtrl::saveUserSet()
{
	return SetAddressValue(0x00060020+0x04, 0x01);
}
int BaslerCamCtrl::selectUserSet(int set_num)
{
	return SetAddressValue(0x00060060+0x04, (0xFF&set_num));
}

// 水平翻转
int BaslerCamCtrl::reverseX(bool reverse)
{
	return SetAddressValue(0x000302C0+0x04, (0xFF&reverse));
}
bool BaslerCamCtrl::get_reverseX()
{
	return 0x01&GetAddressValue(0x000302C0+0x04);
}

// 垂直翻转
int BaslerCamCtrl::reverseY(bool reverse)
{
	return SetAddressValue(0x00030300+0x04, (0xFF&reverse));
}
bool BaslerCamCtrl::get_reverseY()
{
	return 0x01&GetAddressValue(0x00030300+0x04);
}

// Trigger Mode
int BaslerCamCtrl::triggerMode(bool mode)
{
	return SetAddressValue(0x00040100+0x04, mode);
}
bool BaslerCamCtrl::get_triggerMode()
{
	return GetAddressValue(0x00040100+0x04);
}

// Trigger Source
int BaslerCamCtrl::triggerSourceAcquisition(TriggerSource triggerSource)
{
	return SetAddressValue(0x00040140+0x04, triggerSource);
}
int BaslerCamCtrl::get_triggerSourceAcquisition()
{
	return GetAddressValue(0x00040140+0x04);
}

int BaslerCamCtrl::set_AcquisitionFramePeriodEnable(bool is_enable)
{
	return SetAddressValue(0x00040480+0x04, is_enable);
}

// Pixel Format
int BaslerCamCtrl::set_pixelFormat(int val)
{
	/*Name       value
	 *
	 *Mono8      1
	 *Mono10     3
	 *Mono12     5
	 *BayerGR8   10
	 *BayerRG8   11
	 *BayerGB8   12
	 *BayerBG8   13
	 *BayerGR10  14
	 *BayerRG10  15
	 *BayerGB10  16
	 *BayerBG10  17
	 *BayerGR12  18
	 *BayerRG12  20
	 *BayerGB12  22
	 *BayerBG12  24
	 */
	return SetAddressValue(0x00030020+0x04, val);
}
int BaslerCamCtrl::get_pixelFormat()
{
	return GetAddressValue(0x00030020+0x04);
}

void show_basler_cmd_list()
{
	int i = 0;
	printf("%02d. Width   w 0x00030204 800\n",    ++i);
	printf("%02d. Height  w 0x00030224 800\n",    ++i);
	printf("%02d. OffsetX w 0x00030244 256\n",    ++i);
	printf("%02d. OffsetY w 0x00030264 484\n",    ++i);
	printf("%02d. ReverseX w 0x000302C4 <0xFE|0xFF>\n", ++i);
	printf("%02d. ReverseY w 0x00030304 <0xFE|0xFF>\n", ++i);
	printf("%02d. PixelFormat w 0x00030024 <Mono8:1, Mono10:3, Mono12:5, BayerGR8:10, BayerRG8:11, BayerGB8:12, BayerBG8:13, BayerGR10:14, BayerRG10:15, BayerGB10:16, BayerBG10:17, BayerGR12:18, BayerRG12:20, BayerGB12:22, BayerBG12:24>\n", ++i);
	printf("%02d. TriggerMode w 0x00040104 <0|1>\n", ++i);
	printf("%02d. TriggerSource w 0x00040144 <SOFTWARE:0, LINE1:1, CC1:9>\n", ++i);
	printf("%02d. ExposureTime w 0x00040464 2500\n",  ++i);
	printf("%02d. ExposureAuto w 0x00040424 <0:off, 1:once, 2:continues>\n", ++i);
	printf("%02d. GainAbsVal w 0x00020024 <33-512>\n",      ++i);
	printf("%02d. GainCtrl w 0x00020004 0\n",     ++i);
	printf("%02d. DeviceTapGeometry w 0x00000724 <1tap:0, 2tap:1, 3tap:7, 8tap:12, 10tap:14>\n", ++i);
	printf("%02d. AcquisitionFrameRate w 0x00040484 0\n", ++i);
	printf("%02d. TargetGray w 0x00070004 127\n",  ++i);
	printf("%02d. AutoExposureTimeUpperLimit w 0x00070084 2500\n", ++i);
	printf("%02d. AutoExposureTimeLowerLimit w 0x00070064 500\n", ++i);
	printf("%02d. UserSetSelect w 0x00060064 1\n",   ++i);
	printf("%02d. UserSetSave w 0x00060024 1\n",   ++i);
}

/**
 * @brief basler相机控制调试命令
 * @param argc
 * @param argv[]
 * @return 0 - success
 */
int shell_basler_camer_cmd(int argc, const char *argv[])
{
	uint32_t address = 0;
	uint32_t value = 0;

	if (!g_basler_cam) {
		printf("error: camera is NOT link!\n");
		return -1;
	}
	if (argc < 2) {
		printf("Usage: %s <r|w|help> <address> <value>\n", argv[0]);
		return -2;
	}

	if (!strcmp(argv[1], "r") ) {
		sscanf(argv[2], "%x", &address);
		value = g_basler_cam->GetAddressValue(address);
		printf("Basler get val=0x%x from addr:0x%x\n", value, address);
	} else if (!strcmp(argv[1], "w") ) {
		if (argc != 4) {
			printf("Usage: %s <r|w> <address> <value>\n", argv[0]);
			return -2;
		}
		sscanf(argv[2], "%x", &address);
		sscanf(argv[3], "%x", &value);
		g_basler_cam->SetAddressValue(address, value);
		printf("Basler set val=0x%x to addr:0x%x\n", value, address);
	} else if (!strcmp(argv[1], "help")) {
		show_basler_cmd_list();
	} else {
		printf("Usage: %s <r|w|help> <address> <value>\n", argv[0]);
		return -2;
	}

	return 0;
}
FINSH_FUNCTION_EXPORT(shell_basler_camer_cmd, send cmd to basler camera)

SPDK_LOG_REGISTER_COMPONENT(cam_basler_ctrl)
